//////////////////////////////////////////////////////////////////////////
#include "Scene.h"
#include <hge.h>
#include "../Texture/Texture.h"
#include "../GameObject/GameObject.h"
#include "../Sprite/Sprite.h"
#include <cassert>
#include <Box2D/Box2D.h>
#include <cstring>
#include <pugixml.hpp>
//////////////////////////////////////////////////////////////////////////
#define		PHYS_VELOCITY_ITERATIONS  6
#define		PHYS_POSITITON_ITERATIONS 6
#define		PHYS_UPDATE_DELTA_TIME    0.0166f // ~60FPS
//////////////////////////////////////////////////////////////////////////
extern HGE *hge;
//////////////////////////////////////////////////////////////////////////
Scene::Scene( const char* xmlPath )
: m_physWorld(NULL)
, m_physDeltaTime(0.0f)
{
	parseXml(xmlPath);
}
//////////////////////////////////////////////////////////////////////////
Scene::~Scene()
{
	if(m_physWorld)
	{
		delete m_physWorld;
		m_physWorld = NULL;
	}
}
//////////////////////////////////////////////////////////////////////////
void Scene::update( float dt )
{
	if(m_physWorld)
	{
		m_physDeltaTime += dt;
		while(m_physDeltaTime >= PHYS_UPDATE_DELTA_TIME)
		{
			m_physWorld->Step(PHYS_UPDATE_DELTA_TIME, PHYS_VELOCITY_ITERATIONS, PHYS_POSITITON_ITERATIONS);
			m_physDeltaTime -= PHYS_UPDATE_DELTA_TIME;
		}
	}

	CompositeObject::update(dt);
}
//////////////////////////////////////////////////////////////////////////
void Scene::createPhysWorld()
{
	m_physWorld = new b2World(b2Vec2(0, 10), true);
}
//////////////////////////////////////////////////////////////////////////
void Scene::parseXml(const char* xmlPath)
{
	//int objNum = hge->Ini_GetInt("SCENE", "objects_count", 0);

	//if(objNum == 0)
	//	return;

	//for(int i = 0; i < objNum; ++i)
	//{
	//	GameObject* obj = new GameObject();

	//	char sectionName[128];
	//	std::sprintf(sectionName, "OBJECT%d", i);

	//	const char* name = hge->Ini_GetString(sectionName, "name", "");
	//	obj->setName(name);

	//	const char* textureName = hge->Ini_GetString(sectionName, "sprite", "");
	//	if(strlen(textureName))
	//		obj->setSprite(textureName);

	//	obj->setX(hge->Ini_GetFloat(sectionName, "x", 0));
	//	obj->setY(hge->Ini_GetFloat(sectionName, "y", 0));

	//	bool physBody	  = m_physWorld ? !!hge->Ini_GetInt(sectionName, "phys_body", 0) : false;
	//	if(physBody)
	//	{
	//		float density = hge->Ini_GetFloat(sectionName, "phys_density", 0.1f);
	//		bool staticBody = !!hge->Ini_GetInt(sectionName, "phys_static", 0);
	//		obj->createPhysBody(m_physWorld, density, staticBody);
	//	}

	//	addChild(obj);
	//}

	pugi::xml_document xmlDoc;

	pugi::xml_parse_result openResult = xmlDoc.load_file(xmlPath);
	assert(openResult);
	if(!openResult)
		return;

	pugi::xml_node sceneNode = xmlDoc.child(L"Scene");
	pugi::xml_attribute withPhys = sceneNode.attribute(L"Physics");
	if(withPhys && withPhys.as_bool())
		createPhysWorld();
	
	for(pugi::xml_node subNode = sceneNode.child(L"Node"); subNode; subNode = subNode.next_sibling(L"Node"))
	{
		CompositeObject* node = loadXmlNode(&subNode);
		assert(node);
		if(node)
			addChild(node);
	}
}
//////////////////////////////////////////////////////////////////////////
CompositeObject* Scene::loadXmlNode( pugi::xml_node* xmlNode )
{
	pugi::xml_attribute nodeType = xmlNode->attribute(L"Type");

	assert(nodeType);
	if(!nodeType)
		return NULL;
	
	CompositeObject* newNode = NULL;

	std::wstring typeStr = nodeType.value();
	if(typeStr == L"Sprite")
	{
		newNode = createSprite(xmlNode);
	}
	else if(typeStr == L"GameObject")
	{
		newNode = createGameObject(xmlNode);
	}

	assert(newNode);
	if(!newNode)
		return NULL;

	for(pugi::xml_node subNode = xmlNode->child(L"Node"); subNode; subNode = subNode.next_sibling(L"Node"))
	{
		CompositeObject* node = loadXmlNode(&subNode);
		newNode->addChild(node);
	}

	return newNode;
}
//////////////////////////////////////////////////////////////////////////
CompositeObject* Scene::createSprite( pugi::xml_node* xmlNode )
{
	pugi::xml_attribute filePath = xmlNode->attribute(L"Path");
	assert(filePath);
	if(!filePath)
		return NULL;

	Sprite* sprite = new Sprite();

	sprite->setTexturePath(filePath.value());

	pugi::xml_attribute centerAlign = xmlNode->attribute(L"CenterAlign");
	if(centerAlign && centerAlign.as_bool())
		sprite->setCenterAlign();

	addNodeCommonParams(sprite, xmlNode);

	return sprite;
}
//////////////////////////////////////////////////////////////////////////
void Scene::addNodeCommonParams( CompositeObject* node, pugi::xml_node* xmlNode )
{
	pugi::xml_attribute nodeName = xmlNode->attribute(L"Name");
	if(nodeName)
	{
		node->setName(nodeName.value());
	}

	pugi::xml_attribute nodeCoords = xmlNode->attribute(L"Coords");
	if(nodeCoords)
	{
		std::wstring strCoords = nodeCoords.value();
		size_t pos = strCoords.find(';');

		assert(pos != std::wstring::npos);
		if(pos != std::wstring::npos)
		{
			float x = static_cast<float>(_wtof(strCoords.substr(0, pos).c_str()));
			float y = static_cast<float>(_wtof(strCoords.substr(pos+1, strCoords.length()-pos).c_str()));
			node->setXY(x, y);
		}
	}

	pugi::xml_attribute nodeAngle = xmlNode->attribute(L"Angle");
	if(nodeAngle)
		node->setAngle(nodeAngle.as_float());
}
//////////////////////////////////////////////////////////////////////////
CompositeObject* Scene::createGameObject( pugi::xml_node* xmlNode )
{
	GameObject* gameObject = new GameObject();
	
	pugi::xml_attribute nodeSprite = xmlNode->attribute(L"Sprite");
	if(nodeSprite)
	{
		gameObject->setSprite(nodeSprite.value());
	}
	
	addNodeCommonParams(gameObject, xmlNode);

	if(m_physWorld && xmlNode->attribute(L"PhysBody").as_bool())
	{
		pugi::xml_attribute xmlDensity = xmlNode->attribute(L"PhysDensity");
		float density = xmlDensity ? xmlDensity.as_float() : 0.1f;
		
		pugi::xml_attribute xmlStatic = xmlNode->attribute(L"PhysStatic");
		bool static_ = xmlStatic ? xmlStatic.as_bool() : false;

		pugi::xml_attribute xmlSleep = xmlNode->attribute(L"PhysSleep");
		bool sleep = xmlSleep ? xmlSleep.as_bool() : false;

		gameObject->createPhysBody(m_physWorld, density, static_, sleep);
	}

	return gameObject;
}

//////////////////////////////////////////////////////////////////////////